package com.yugao.fintech.draper.admin.service.impl;

import cn.hutool.core.convert.Convert;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yugao.fintech.draper.admin.convert.MenuConvert;
import com.yugao.fintech.draper.admin.entity.SysMenu;
import com.yugao.fintech.draper.admin.entity.SysUser;
import com.yugao.fintech.draper.admin.mapper.MenuMapper;
import com.yugao.fintech.draper.admin.param.MenuDTO;
import com.yugao.fintech.draper.core.model.TreeNode;
import com.yugao.fintech.draper.admin.service.AppInfoService;
import com.yugao.fintech.draper.admin.service.MenuService;
import com.yugao.fintech.draper.admin.service.RoleService;
import com.yugao.fintech.draper.admin.service.UserService;
import com.yugao.fintech.draper.core.util.TreeUtil;
import com.yugao.fintech.draper.core.exception.BaseException;
import com.yugao.fintech.draper.security.SecurityUser;
import com.yugao.fintech.draper.security.util.SecurityUserUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

@Slf4j
@Service
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class MenuServiceImpl extends ServiceImpl<MenuMapper, SysMenu> implements MenuService {
    @Autowired
    private MenuMapper menuMapper;
    @Autowired
    private RoleService roleService;
    @Autowired
    private UserService userService;
    @Autowired
    private AppInfoService appInfoService;

    @Override
    public Set<String> findUserPermissions(String username) {
        List<MenuDTO> userPermissions = MenuConvert.INSTANCE.convertList(menuMapper.findUserPermissions(username));
        return userPermissions.stream().map(MenuDTO::getPerms).collect(Collectors.toSet());
    }

    @Override
    public List<TreeNode<MenuDTO>> getUserRouters(String username) {
        // 指定用户名角色下的所有菜单
        List<SysMenu> sysMenuList = menuMapper.findUserMenus(username);
        // 根据菜单构建VueRouter
        List<TreeNode<SysMenu>> routes = sysMenuList.stream().map(SysMenu::toVueRouter).collect(Collectors.toList());
        // 构建树形VueRouter
        return MenuConvert.INSTANCE.convert(TreeUtil.buildVueRouter(routes));
    }

    /**
     * 获取全部的树形菜单信息(包括按钮)
     *
     * @return 树形菜单信息
     */
    @Override
    public List<TreeNode<MenuDTO>> getAllMenuTree() {
        List<TreeNode<SysMenu>> menuRoute = menuMapper.getMenuByPid(0L);
        return MenuConvert.INSTANCE.convert(menuRoute);
    }

    /**
     * 获取菜单下拉框
     *
     * @return 树形菜单下拉框
     */
    @Override
    public List<TreeNode<MenuDTO>> getTreeSelect() {
        SecurityUser user = Optional.ofNullable(SecurityUserUtil.getCurrentUser()).orElseThrow(() -> new BaseException("用户未登录！"));
        List<TreeNode<MenuDTO>> allMenuTree = this.getUserRouters(user.getUsername());
        TreeNode<MenuDTO> router = new TreeNode<MenuDTO>().setId("0").setTitle("顶级菜单").setChildren(allMenuTree);
        List<TreeNode<MenuDTO>> result = new ArrayList<>();
        result.add(router);
        return result;
    }

    /**
     * 保存路由信息
     *
     * @param treeNode
     */
    @Override
    public void saveMenu(TreeNode treeNode) {
        SysMenu sysMenu = new SysMenu();
        sysMenu.setHidden(treeNode.getHidden());
        sysMenu.setParentId(Convert.toLong(treeNode.getParentId()));
        sysMenu.setPerms(treeNode.getPerms());
        sysMenu.setTitle(treeNode.getTitle());
        sysMenu.setKeepAlive(Convert.toInt(treeNode.getKeepAlive()));
        sysMenu.setType(treeNode.getType());
        sysMenu.setName(treeNode.getName());
        sysMenu.setComponent(treeNode.getComponent());
        sysMenu.setPath(treeNode.getPath());
        sysMenu.setIcon(treeNode.getIcon());
        sysMenu.setRedirect(treeNode.getRedirect());
        sysMenu.setOrderNum(Convert.toInt(treeNode.getOrderNum()));
        sysMenu.setApplication(treeNode.getApplication());
        menuMapper.insert(sysMenu);
    }

    /**
     * 根据id查询路由信息
     *
     * @param id
     * @return
     */
    @Override
    public TreeNode<SysMenu> getMenuById(Long id) {
        SysMenu sysMenu = menuMapper.selectById(id);
        return sysMenu.toVueRouter();
    }

    /**
     * 更新路由
     */
    @Override
    public void updateMenu(TreeNode treeNode) {
        SysMenu sysMenu = MenuConvert.INSTANCE.convert(treeNode);
        menuMapper.updateById(sysMenu);
        SysMenu menu = new SysMenu().setApplication(sysMenu.getApplication());
        menuMapper.update(menu, Wrappers.<SysMenu>lambdaUpdate().eq(SysMenu::getParentId, sysMenu.getId()));
    }

    @Override
    public Map<String, Object> getUserRoutersAndAuthority() {
        // 返回结果集
        Map<String, Object> result = new HashMap<>(4);
        SecurityUser user = SecurityUserUtil.getCurrentUser();
        Optional.ofNullable(user).map(SecurityUser::getUsername).ifPresent(userName -> {
            // 构建用户路由对象
            CompletableFuture<Void> routes = CompletableFuture
                    .runAsync(() -> result.put("routes", getUserRouters(userName)));
            // 封装用户权限信息
            CompletableFuture<Void> permissions = CompletableFuture
                    .runAsync(() -> result.put("permissions", user.getAuthorities().toArray()));
            // 封装应用信息
            CompletableFuture<Void> apps = CompletableFuture.runAsync(() -> result.put("apps", appInfoService.findAll()));

            // 异步执行
            CompletableFuture.allOf(routes, permissions, apps).join();
        });

        return result;
    }

    @Override
    public List<TreeNode<MenuDTO>> getUserMenuTree(Long userId) {
        SysUser user = userService.getById(userId);
        if (roleService.isSuperAdmin(user.getRoleId())) {
            return getAllMenuTree();
        } else {
            return getUserRouters(user.getUsername());
        }
    }

    @Override
    public void removeById(Long id) {
        menuMapper.deleteById(id);
    }
}
